package org.dyndns.opendemogroup.optimizer.selections;

import java.util.Random;

import org.dyndns.opendemogroup.optimizer.IOptimizationProblem;
import org.dyndns.opendemogroup.optimizer.ISelection;
import org.dyndns.opendemogroup.optimizer.Member;
import org.dyndns.opendemogroup.optimizer.Pair;
import org.dyndns.opendemogroup.optimizer.Population;

/**
 * Implemented as per the description on page 4 of the assignment hand-out, with
 * elements from slides 12-15 of <code>howGAsWork1.pdf</code>.
 */
public class Rank implements ISelection
{

	private final double probability;

	private int[] firstSelections = null;

	private int[] secondSelections = null;

	public Rank ( double probability )
	{
		this.probability = probability;
	}

	/**
	 * @see ISelection#select(java.util.Random, Population,
	 *      IOptimizationProblem, int)
	 */
	public Pair<Member, Member> select ( Random randomSource,
			Population population, IOptimizationProblem problem, int loopIndex )
	{
		final int currentIndex = loopIndex / 2;

		Pair<Member, Member> result = new Pair<Member, Member> ( );
		result.first = population.members[firstSelections[currentIndex]];
		result.second = population.members[secondSelections[currentIndex]];
		return result;
	}

	public void reset ( Random randomSource, Population population,
			IOptimizationProblem problem )
	{
		// { initialization
		final int populationSize = population.getPopulationSize ( );
		if ( null == firstSelections )
		{
			firstSelections = new int[populationSize / 2];
			secondSelections = new int[populationSize / 2];
		}
		int start;
		int end;
		int direction;
		if ( problem.isMaximizing ( ) )
		{

			start = populationSize - 1;
			end = -1;
			direction = -1;
		}
		else
		{
			start = 0;
			end = populationSize - 1;
			direction = 1;
		}
		// }

		int numberOfSelectedItems = 0;
		double r = randomSource.nextDouble ( );
		double sum = 0;
		int i = start;
		for ( int power = 1; i != end && numberOfSelectedItems < populationSize; i +=
			direction, power++ )
		{
			final double term =
				Math.pow ( probability, power ) * populationSize;
			sum += term;
			while ( sum > r && numberOfSelectedItems < populationSize )
			{
				final int currentIndex = numberOfSelectedItems / 2;
				secondSelections[currentIndex] = firstSelections[currentIndex];
				firstSelections[currentIndex] = i;
				r++;
				numberOfSelectedItems++;
			}
		}
	}

}
